<?php
namespace Handler;

use Core\DzInternetProtocol;
use Core\TanbaSwooleTable;
use Core\Model;
use Core\TbRedis;
use Helper\Log;

class Upgrade
{
    //客户端升级信息
    protected $upgrade = array();

    //心跳包定时器列表
    protected $heart_timer = array();

    //协议生成对象
    protected $protocol;
    private $client;

    /**
     * 构造函数
     */
    public function __construct()
    {
       //协议生成对象
        $this->protocol = new DzInternetProtocol();

        //创建客户端连接内存表
        $this->client = new  \Swoole\table(1024);
        $this->client->column('frimware', \Swoole\table::TYPE_STRING, 128);
        $this->client->column('deviceID', \Swoole\table::TYPE_STRING, 64);
        $this->client->column('packlength', \Swoole\table::TYPE_INT, 8);
        $this->client->column('packcounts', \Swoole\table::TYPE_INT, 4);
        $this->client->column('timeout', \Swoole\table::TYPE_STRING, 20);

        $this->client->create();

    }

    /**
     * 主进程启动回调方法
     * @param  Swoole\Server $serv    swoole对象
     */
    public function onStart($serv) {}

    /**
     * manage进程启动方法
     * @param $serv
     */
    public function onManagerStart($serv) {
        echo " upgrade  worker start \n";
    }

    /**
     * worker停止回调函数
     * @param $serv
     * @param $worker_id
     */
    public function onWorkerStop($serv, $worker_id) {}

    /**
     * 服务器关闭函数
     * @param $serv
     */
    public function onShutdown( $serv ) {}

    /**
     * 任务启动回调函数
     * @param $serv
     * @param $task_id
     * @param $from_id
     * @param $data
     */
    public function onTask($serv, $task_id, $from_id, $data ) {}


    /**
     * 任务完成函数
     * @param $serv
     * @param $task_id
     * @param $data
     */
    public function onFinish($serv, $task_id, $data) {}

    /**
     * worker进程启动回调方法
     * @param  Swoole\Server $serv    swoole对象
     * @param  int $worker_id         worker进程id号
     */
    public function onWorkerStart($serv, $worker_id) {
        //连接数据库
        while (true) {
            try {
                $serv->db[$worker_id] = Model::init(PRERDS."upsvdb".$worker_id);
                break;
            } catch (\PDOException $e) {
                Log::print('mysql connect fail on worker['.$serv->worker_id.']', $e->getCode(), $e->getLine());
            }
        }

        //连接redis
        while (true) {
            try {
                $serv->redis[$worker_id] =TbRedis::init(PRERDS."upsvdb.".$worker_id);
                break;
            } catch (\PDOException $e) {
                Log::print('redis connect fail on worker['.$serv->worker_id.']', $e->getCode(), $e->getLine());
            }
        }

    }

    /**
     * 连接回调方法
     * @param  Swoole\Server $serv    swoole对象
     * @param  int $fd                连接句柄编号
     */
    public function onConnect($serv, $fd) {
        $info = $serv->connection_info($fd);
        echo "onConnect :worker_id=$serv->worker_id,fd=$fd,from={$info['remote_ip']}:{$info['remote_port']} \n";
    }

    /**
     * 接收数据回调方法
     * @param  Swoole\Server $serv    swoole对象
     * @param  int $fd                连接句柄编号
     * @param  int $form_id           reactor线程号
     * @param  bin $data              接收到的二进制数据
     */
    public function onReceive($serv, $fd, $form_id, $data)
    {
        $info = $serv->connection_info($fd);
        $theredis= $serv->redis[$serv->worker_id];
        $theDb=$serv->db[$serv->worker_id];

        $hexStr=bin2hex($data);
        echo  PHP_EOL.'receive:'.$hexStr.PHP_EOL;

        //解析数据
        $token=time();
        $data = $this->protocol->upgradeparsein($hexStr,$token);
//
        if(!$data || !isset($data['data']) || !isset($data['header'])) return false;

        if ($data['header']['dt']=='01'){
            if($data['header']['sdt']=='0011'){
                var_dump($data['data']);
                if(isset($data['data']['fromcmd']) ){
                    echo PHP_EOL.$fd." return:".$data['data']['fromcmd'].PHP_EOL;
                    switch ($data['data']['fromcmd']) {
                        case 'askupdatedata':
                            if(isset($data['data']['data']['deviceID']) && isset($data['data']['data']['frimware'])){

                                $deviceID=$data['data']['data']['deviceID'];

                                $file = config('uploader'). trim($data['data']['data']['frimware']);
                                if (!file_exists($file) || !is_readable($file)){
                                    echo "file [{$file}] not exists!";
                                    $ifok=2;//取消升级
                                    $filesize=0;
                                }else{
                                    $ifok=1;//确认升级
                                    $filesize = abs(filesize($file));
                                }


                                $this->client->set($fd,array(
                                    'deviceID'=>$deviceID,
                                    'timeout' => microtime_float(),
                                    'frimware'=>$data['data']['data']['frimware'],
                                    'packlength'=>$data['data']['data']['packlength'],
                                    'packcounts'=>$filesize==0?0:($filesize/(int)$data['data']['data']['packlength']))
                                );

                                $this->sendWbsckResponse($serv,$fd,array('updatastat'=>11,'stattext'=>'已连接升级服务器','isyes'=>$ifok==1?'确认升级':'取消升级'));

                                $theredis->hMset($deviceID, array('updatastat'=>$ifok==1?11:21));


                                //记录日志 --升级前版本 --升级后版本 --升级请求的文件
                                $datalog = array(
                                    'DeviceId' =>$deviceID,
                                    'fd'=>$fd,
                                    'frimware'=>$data['data']['data']['frimware'],
                                    'Source' => $info['remote_ip'],
                                    'starttime' => time(),
                                    'Status'=>$ifok==1?11:21,
                                    'beforv'=>$theredis->hget($deviceID,'basedeviceinfo'),
                                    'addtime' => time(),
                                );
                                $theDb->insert('device_upgrade_log', $datalog);

//                                $errocounts= $theDb->count('device_upgrade_log', ['DeviceId' =>$deviceID, 'frimware'=>$data['data']['data']['frimware'],'Status[<>]'=>[0,2,3]]);
//                                if($errocounts>3){
//                                    //失败3次取消升级
//                                    if($ifok==1)
//                                        $serv->send($fd, $this->protocol->setCommand(['dt'=>'01','sdt'=>'0010','upgradesv'=>1],['do'=>'askupdatedata','data'=>$ifok],$token));
//
//                                }else{
                                    $serv->send($fd, $this->protocol->setCommand(['dt'=>'01','sdt'=>'0010','upgradesv'=>1],['do'=>'askupdatedata','data'=>$ifok],$token));

//                                }

                            }
                            break;
                        case 'getupdatenum':
                            $deviceID=$this->client->get($fd,'deviceID');
                            $theredis->hMset($deviceID, array('updatastat'=>12));

                            $filename=$this->client->get($fd,'frimware');
                            $packcounts=$this->client->get($fd,'packcounts');

                            $file =config('uploader'). trim($filename);
                            //判断文件是否存在或者可读如果不存在则记录日志
                            if (!file_exists($file) || !is_readable($file)){
                                echo "file [{$file}] not exists!";
                            }else{
                                $this->sendWbsckResponse($serv,$fd,array('updatastat'=>12,'stattext'=>'升级中','nowpacknum'=>$data['data']['data'],'totalpack'=>$packcounts));

                                $update=array('packlength'=>$this->client->get($fd,'packlength'),'file'=>$file,'packnum'=>$data['data']['data']);
                                $serv->send($fd, $this->protocol->setCommand(['dt'=>'01','sdt'=>'0010','upgradesv'=>1],['do'=>'getupdatenum','data'=>$update],$token));
                            }
                            break;
                        case 'endupdate':
                            $deviceID=$this->client->get($fd,'deviceID');
                            $frimware=$this->client->get($fd,'frimware');

                            $theredis->hMset($deviceID, array('updatastat'=>$data['data']['code']));
                            $this->sendWbsckResponse($serv,$fd,array('statcode'=>$data['data']['code'],'stattext'=>$data['data']['data']));

                            $dataup = array(
                                'endtime' => time(),
                                'Status'=>$data['data']['code']
                            );

                            $theDb->updata('device_upgrade_log', $dataup,['DeviceId' =>$deviceID,'frimware'=>$frimware]);

                            echo "升级结果通知：\n";
                            var_dump($data['data']['data']);
                            break;
                        default:
                            echo "异常数据";
                            var_dump($data['data']['data']);
                            break;
                    }

                }else{

                        var_dump($data);
                }

            }

        }

    }

    public function sendWbsckResponse($serv,$fd,$data){
            $time0= $this->client->get($fd,  'timeout');
            $deviceID=$this->client->get($fd, 'deviceID');

            $ret = array(
                'type' => 'update',
                'command'=>'setupdate',
                'wsfd' => 0,
                'deviceID' => $deviceID,
                'message' => '升级结果',
                'msg' => '返回结果'.json_encode($data,JSON_UNESCAPED_UNICODE),
                'token' => 0,
                'after' =>'',
                'time' => microtime_float()-(int)$time0,
                'rawdata'=>'',
                'returntime'=>date('Y-m-d H:i:s',time())

            );

            $serv->redis[$serv->worker_id]->lPush('result', json_encode($ret));//推送到前端

    }


    /**
     * 关闭回调方法
     * @param  Swoole\Server $serv    swoole对象
     * @param  int $fd                连接句柄编号
     */
    public function onClose($serv, $fd, $reactorId)
    {
        if($reactorId < 0) {
            echo "server close " . $fd;
        } else {
            echo "client close " . $fd;
        }
        unset($this->upgrade[$fd]);
    }

    /**
     * 关机函数
     */
    public function shutdown()
    {
        echo "php shutdown \n";
    }
}